]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
shell/app: Add method to activate an action via FDO DBus activation
authorJulian Sparber <julian@sparber.net>
Mon, 19 Feb 2024 08:30:25 +0000 (09:30 +0100)
committerJulian Sparber <julian@sparber.net>
Wed, 28 Feb 2024 10:27:40 +0000 (11:27 +0100)
There are multiple different places where we activate an app action,
but none set the correct activation token. Therefore this adds an async
method to to call `ActivateAction` on `org.freedesktop.Appliaction`
with the correct activation token/startup id as platform data.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3198>

js/ui/environment.js
src/shell-app.c
src/shell-app.h

index 6a2b60864293f04512d49fac38e39e696ce09a2c..4150542db917d56029d8e9790092338ce384d4a9 100644 (file)
@@ -30,6 +30,7 @@ Gio._promisify(Gio.DBusProxy.prototype, 'init_async');
 Gio._promisify(Gio.DBusProxy.prototype, 'call_with_unix_fd_list');
 Gio._promisify(Gio.File.prototype, 'query_info_async');
 Gio._promisify(Polkit.Permission, 'new');
+Gio._promisify(Shell.App.prototype, 'activate_action');
 
 // We can't import shell JS modules yet, because they may have
 // variable initializations, etc, that depend on this file's
index 371bc6cf7625d123c3917fcd24637809315abb3d..3b5df0aabbac4e07705f831d9cbe9cc28e1e8494 100644 (file)
@@ -1447,6 +1447,196 @@ shell_app_launch_action (ShellApp        *app,
   g_object_unref (context);
 }
 
+static gchar *
+object_path_from_app_id (const gchar *app_id)
+{
+  gchar *app_id_path, *iter;
+
+  app_id_path = g_strconcat ("/", app_id, NULL);
+  for (iter = app_id_path; *iter; iter++)
+    {
+      if (*iter == '.')
+        *iter = '/';
+
+      if (*iter == '-')
+        *iter = '_';
+    }
+
+  return app_id_path;
+}
+
+static GVariant *
+get_platform_data (ShellApp *app,
+                   guint     timestamp,
+                   int       workspace)
+{
+  GVariantBuilder builder;
+  ShellGlobal *global;
+  g_autoptr (GAppLaunchContext) context = NULL;
+  gchar *startup_id;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+  if (!app->info)
+    return g_variant_builder_end (&builder);
+
+  global = shell_global_get ();
+  context = shell_global_create_app_launch_context (global, timestamp, workspace);
+
+  if (!context)
+    return g_variant_builder_end (&builder);
+
+  startup_id = g_app_launch_context_get_startup_notify_id (context, G_APP_INFO (app->info), NULL);
+
+  if (!startup_id)
+    return g_variant_builder_end (&builder);
+
+
+  g_variant_builder_add (&builder, "{sv}",
+      "desktop-startup-id", g_variant_new_string (startup_id));
+  g_variant_builder_add (&builder, "{sv}",
+      "activation-token", g_variant_new_take_string (g_steal_pointer (&startup_id)));
+
+  return g_variant_builder_end (&builder);
+}
+
+static void
+on_activate_action_cb (GObject      *source,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+  GTask *task = G_TASK (user_data);
+  g_autoptr (GVariant) value = NULL;
+  g_autoptr (GError) error = NULL;
+
+  value = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
+                                         res, &error);
+
+  if (error)
+      g_task_return_error (task, g_steal_pointer (&error));
+  else
+      g_task_return_boolean (task, TRUE);
+}
+
+static void
+activate_action_get_bus_cb (GObject      *object,
+                            GAsyncResult *result,
+                            gpointer      user_data)
+{
+  GTask *task = G_TASK (user_data);
+  ShellApp *app = NULL;
+  g_autoptr (GDBusConnection) session_bus = NULL;
+  g_autoptr (GError) error = NULL;
+  g_autofree gchar *object_path = NULL;
+  g_autofree gchar *app_id = NULL;
+  gchar *last_dot;
+
+  session_bus = g_bus_get_finish (result, &error);
+
+  if (error)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  app = SHELL_APP (g_task_get_source_object (task));
+
+  app_id = g_strdup (g_app_info_get_id (G_APP_INFO (app->info)));
+  last_dot = strrchr (app_id, '.');
+  if (last_dot && g_str_equal (last_dot, ".desktop"))
+      *last_dot = '\0';
+
+  object_path = object_path_from_app_id (app_id);
+
+  g_dbus_connection_call (session_bus,
+                           app_id, object_path,
+                           "org.freedesktop.Application", "ActivateAction",
+                           g_task_get_task_data (task),
+                           NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+                           g_task_get_cancellable (task),
+                           on_activate_action_cb, task);
+}
+
+
+/**
+ * shell_app_activate_action
+ * @app: the #ShellApp
+ * @action_name: the name of an action to activate
+ * @parameter: (nullable): the parameter to the activation
+ * @timestamp: Event timestamp, or 0 for current event timestamp
+ * @workspace: Start on this workspace, or -1 for default
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: (scope async): A #GAsyncReadyCallback to call when the request is satisfied.
+ * @user_data: (nullable): User data to pass to @callback
+ *
+ * This activates an action using 'org.freedesktop.Application' DBus interface.
+ *
+ * This function will fail if this #ShellApp doesn't have a valid #GDesktopAppInfo
+ * with a valid id.
+ */
+void
+shell_app_activate_action (ShellApp            *app,
+                           const char          *action_name,
+                           GVariant            *parameter,
+                           guint                timestamp,
+                           int                  workspace,
+                           GCancellable        *cancellable,
+                           GAsyncReadyCallback  callback,
+                           gpointer             user_data)
+{
+  g_autoptr (GTask) task = NULL;
+  g_autoptr (GVariant) task_data = NULL;
+
+  g_return_if_fail (SHELL_IS_APP (app));
+  g_return_if_fail (G_IS_DESKTOP_APP_INFO (app->info));
+  g_return_if_fail (g_application_id_is_valid (g_app_info_get_id (G_APP_INFO (app->info))));
+  g_return_if_fail (action_name != NULL && action_name[0] != '\0');
+  g_return_if_fail (parameter == NULL || g_variant_is_of_type (parameter,  G_VARIANT_TYPE ("av")));
+  g_return_if_fail (timestamp >= 0);
+  g_return_if_fail (workspace >= -1);
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (app, cancellable, callback, user_data);
+  g_task_set_source_tag (task, shell_app_activate_action);
+
+  if (!parameter)
+    parameter = g_variant_new("av", NULL);
+
+  task_data = g_variant_ref_sink (g_variant_new ("(s@av@a{sv})", action_name, parameter,
+                                                 get_platform_data (app, timestamp, workspace)));
+
+  g_task_set_task_data (task, g_steal_pointer (&task_data), (GDestroyNotify) g_variant_unref);
+
+  g_bus_get (G_BUS_TYPE_SESSION, cancellable, activate_action_get_bus_cb, g_steal_pointer (&task));
+}
+
+/**
+ * shell_app_activate_action_finish:
+ * @app: the #ShellApp
+ * @error: #GError for error reporting
+ *
+ * Finish the asynchronous operation started by shell_app_activate_action()
+ * and obtain its result.
+ *
+ * Returns: whether the operation was successful
+ *
+ */
+gboolean
+shell_app_activate_action_finish (ShellApp      *app,
+                                  GAsyncResult  *result,
+                                  GError       **error)
+{
+  g_return_val_if_fail (SHELL_IS_APP (app), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+  g_return_val_if_fail (g_async_result_is_tagged (result,
+                                                  shell_app_activate_action),
+                        FALSE);
+
+  if (!g_task_propagate_boolean (G_TASK (result), error))
+    return FALSE;
+
+  return TRUE;
+}
+
 /**
  * shell_app_get_app_info:
  * @app: a #ShellApp
index bf6f45ecb2043b9bdfdd37ac1c196193e3313636..a3e087eaed67d0a337a253ade223333c8947c057 100644 (file)
@@ -69,6 +69,19 @@ void shell_app_launch_action (ShellApp        *app,
                               guint            timestamp,
                               int              workspace);
 
+void shell_app_activate_action (ShellApp            *app,
+                                const char          *action_name,
+                                GVariant            *parameter,
+                                guint                timestamp,
+                                int                  workspace,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data);
+gboolean
+shell_app_activate_action_finish (ShellApp      *app,
+                                  GAsyncResult  *result,
+                                  GError       **error);
+
 int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
 
 int shell_app_compare (ShellApp *app, ShellApp *other);